/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package webserver;

import java.util.ArrayList;

/**
 * This class provides a Pool of Threads which can be used to perform multiple actions
 * Additionally a Queue of work which should be done will be managed
 * The scheduled work (elements in the Queue) will be performed by the Threads in this Pool
 * 
 */
public class ThreadPool {

	// Variables
	private volatile Queue<Runnable> todo;
	private ArrayList<ThreadPoolWorker> workers;
	private int size;
	private boolean finish = false;

	/**
	 * ThreadPool Default Constructor
	 */
	public ThreadPool() {
		this(5);
	}

	/**
	 * ThreadPool Overloaded Constructor
	 * @param size - The number of Threads which should be present in the Pool
	 */
	public ThreadPool(int size) {
		this.size = size;
		this.todo = new Queue<Runnable>();
		this.workers = new ArrayList<ThreadPoolWorker>(size);

		// Check if the size is correct
		if (size <= 0) {
			throw new IllegalArgumentException("Size must be greater than zero (size > 0)!");
		}

		// Create the given number of WorkerThreads
		for (int i = 0; i < size; i++) {
			this.workers.add(new ThreadPoolWorker(this));
		}
	}

	/**
	 * Check whether a Worker is currently idle
	 * @return boolean - any worker is idle?
	 */
	synchronized boolean isWorkerAvailable() {
		// Check each Worker if he is idle
		for (ThreadPoolWorker tpw : this.workers) {
			if (tpw.isIdle()) {
				return true;
			}
		}

		// No worker idle ..
		return false;
	}

	/**
	 * Check if currently some Work is available to be executed
	 * @return boolean
	 */
	synchronized boolean isWorkAvailable() {
		return (this.todo.getSize() > 0);
	}

	/**
	 * Get new Work to do
	 * Will wait until new work is available
	 * @return Runnable - The work which should be done
	 */
	synchronized Runnable getNewWork() throws IllegalStateException {
		while (this.todo.getSize() == 0) {
			try {
				this.wait(5000);
			} catch (InterruptedException ie) {
				Logger.writeToLog("getNewWork(): Interrupted" + ie.toString());
			}
		}

		return this.todo.getNextPendingElement();
	}

	/**
	 * Mark a ThreadPoolWorker as "finished"
	 * @param tpw - The Worker which finished
	 */
	synchronized void workerFinished(ThreadPoolWorker tpw) {
		if (this.workers.contains(tpw)) {
			this.workers.remove(tpw);

			// Recreate workers if there are not as much as desired
			while (this.workers.size() < this.size && !this.finish) {
				this.workers.add(new ThreadPoolWorker(this));
			}
		}
	}

	/**
	 * Add a new Task to the Work Queue
	 * @param r - The task which should be added
	 */
	synchronized void addToWorkQueue(Runnable r) {
		this.todo.enqueue(r);
		this.notify();
	}

	/**
	 * Tell all available Workers in this Pool to finish ASAP
	 */
	public void finishAll() {
		this.finish = true;
		while (!this.workers.isEmpty()) {
			this.workers.get(0).finish();
		}
	}
}
